iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0
生成式 AI

AI 產品與架構設計之旅:從 0 到 1,再到 Day 2系列 第 13

Day 13: Scaleibility - 微服務化

  • 分享至 

  • xImage
  •  

問題背景:Socket Mode 的10連線詛咒

前面十二天我們一路從無到有建了個 AI 聊天機器人,現在要準備 deploy 到 production 了,但之前的選擇造成了一些限制:

Slack Socket Mode 每個 App Token 最多只能開 10 條連線!

你沒看錯,就是 10 條!不是 100 條,不是薯條,就是 10 條!

這意味著什麼?看看我們 Day 12 的架構:

// Day 12: 全部塞在一個 process 裡
async function startSlackBolt() {
  // Slack Bot 邏輯
}

async function startGenkitFlow() {
  // GenKit Flow 邏輯  
}

async function main() {
  await Promise.all([
    startGenkitFlow(),
    startSlackBolt()
  ]);
  console.log('Service Started');
}

想要 scale up?每開一個 instance 就吃掉一條 Slack 連線。想開 20 個 instance 提高可用性?對不起,第11個開始就連不上了。

更慘的是,如果你想要調整 GenKit 的 instance 數量來處理更多 AI 請求,結果 Slack 連線也跟著增加。完全是被綁架的狀態啊!

但我們在前面選擇了 Socket Mode,現在就得面對這個挑戰。

解決思路:拆家囉!

既然被 Slack 限制住了,那就拆吧!

仔細分析我們的架構

我們現在有三個主要功能:

  1. Slack 互動:Socket Mode 連線,處理用戶訊息
  2. AI 推理:GenKit 管理 AI Flow,調用 Gemini API
  3. 工具執行:MCP 提供瀏覽器自動化

這三個功能的特性完全不同:

  • Slack:需要穩定連線,但受 10 條限制
  • GenKit:純 API 調用,沒有連線限制,可以依照狀況 scale
  • MCP:吃記憶體,瀏覽器很重,需要獨立 scale

「既然特性不同,為什麼要綁在一起受罪?」

拆分策略:三分天下

最終決定拆成三個獨立服務:

Day 12:                     Day 13:
src/                        ├── GenKit/          
  index.ts                  │   ├── src/
package.json                │   ├── config/
                            │   ├── prompts/
                            │   └── Dockerfile
                            ├── SlackBolt/      
                            │   ├── src/
                            │   └── Dockerfile
                            └── docker-compose.yml

MCP

  • 雖然在程式碼裡沒直接看到,但從 GenKit/config/mcp-config.json 可以看出來:
{
  "mcpServers": {
    "playwright": {
      "url": "http://127.0.0.1:8931/mcp"
    }
  }
}

Playwright 我們改用了 streamable http 模式的 MCP 了。

本地開發的調整:Docker Compose

配置

# docker-compose.yml
services:
  genkit-service:
    build:
      context: ./GenKit
      dockerfile: Dockerfile
    container_name: genkit-service
    ports:
      - "3400:3400"
    environment:
      - SLACK_BOT_USER_ID=${SLACK_BOT_USER_ID}
      - GEMINI_API_KEY=${GEMINI_API_KEY}
      - GENKIT_PORT=3400
    restart: unless-stopped

  slack-service:
    build:
      context: ./SlackBolt
      dockerfile: Dockerfile
    container_name: slack-service
    environment:
      - SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN}
      - SLACK_APP_TOKEN=${SLACK_APP_TOKEN}
      - GENKIT_SERVICE_URL=http://genkit-service:3400
    depends_on:
      - genkit-service
    restart: unless-stopped
    
  playwright-mcp:
    image: mcr.microsoft.com/playwright/mcp
    ports:
      - "8931:8931"
    entrypoint: ["node"]
    command: ["cli.js", "--headless", "--browser", "chromium", "--no-sandbox", "--port", "8931"]
    restart: unless-stopped

新的依賴關係

現在的 calling chain 變成:

用戶 → Slack Service → GenKit Service → MCP Service → 瀏覽器

每個服務都有自己的 container,有自己的 Dockerfile,有自己的 package.json。真正的關注點分離

開發體驗沒有變爛

擔心開發變複雜?看看根目錄的 package.json:

{
  "scripts": {
    "dev": "concurrently \"cd GenKit && bun run dev\" \"cd SlackBolt && bun run dev\""
  },
  "devDependencies": {
    "concurrently": "^9.1.2"
  }
}

還是一個 bun run dev 全部起來,只是現在後面跑的是兩個獨立的 process。

經驗總結:拆分後的新世界

優點1:終於可以各自 scale 了

看看 k8s.yaml 的部分:

# GenKit Service
apiVersion: apps/v1
kind: Deployment
metadata:
  name: genkit-service
spec:
  replicas: 1

---

# Slack Service
apiVersion: apps/v1
kind: Deployment
metadata:
  name: slack-service
spec:
  replicas: 10  # 榨乾 Socket Mode 的極限

現在:

  • Slack 可以開到極限:10個 replica,充分利用連線數
  • GenKit 愛開幾個開幾個:不會被 Slack 拖累綁住,但現在用的是免錢的 API Key 所以就限制在 1
  • MCP 獨立調整:瀏覽器吃記憶體,可以開在高記憶體的機器上

優點2:掛掉不會全死

以前:任何一個功能掛掉 → 整個 bot 重開 → 用戶體驗爛到爆

現在:

  • MCP 掛了:AI 還是可以回答不需要瀏覽器的問題
  • GenKit 掛了:至少 Slack 可以說「AI 暫時維修中,請稍候」
  • Slack 掛了:重開 Slack service 就好,其他服務繼續跑

這就是所謂的 fault isolation,一個掛不會全部陪葬。

優點3:資源配置可以客製化

以前所有功能都要用同樣的機器規格。現在可以:

  • Slack Service:給網路穩定的機器,保持連線品質
  • GenKit Service:給一般的運算資源就夠了,反正就是 API 轉發
  • MCP Service:給高記憶體的機器,讓瀏覽器跑得順

監控各自的 performance

現在三個 service 可以各自監控:

  • Slack:連線穩定度、訊息處理延遲
  • GenKit:API 回應時間、錯誤率
  • MCP:瀏覽器資源使用、工具執行成功率

開發團隊也可以分工

如果是團隊開發:

  • A同學專攻 Slack 互動體驗
  • B同學專攻 AI Flow 邏輯
  • C同學專攻 MCP 工具開發

大家各自在自己的 service 裡面改,不會互相踩到。


總結

從 Day 12 的到 Day 13 的微服務架構,核心就是:

突破 Slack 10 連線限制:GenKit 和 MCP 不再被拖累
各自 scale 到爽:可以依照需求 scale
故障不會全滅:一個掛了其他還能活著
資源配置客製化:每個 service 都能用最適合的 resourse

Docker Compose 在這個轉換中扮演重要角色,讓我們可以:

  • 本地開發還是很簡單(一個指令全開)
  • 模擬 production 的 microservice 架構
  • 為真正的 container orchestration 做準備

完整的原始碼在這裡,現在每次對話都能看到確切的成本消耗!


AI 的發展變化很快,目前這個想法以及專案也還在實驗中。但也許透過這個過程大家可以有一些經驗和想法互相交流,歡迎大家追蹤這個系列。

也歡迎追蹤我的 Threads @debuguy.dev


上一篇
Day 12: 成本透明化 - 問一個問題多少錢?
下一篇
Day 14: Virtual Key - 不該直接用 Gemini API Key 上線
系列文
AI 產品與架構設計之旅:從 0 到 1,再到 Day 214
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言